package com.hhd.flow.engine.cmd.impl.virtual;

import com.alibaba.fastjson.JSONObject;
import com.hhd.flow.engine.FlowEngine;
import com.hhd.flow.engine.cmd.FlowCommandExecutor;
import com.hhd.flow.engine.utils.CommonUtils;
import com.hhd.flow.engine.utils.FlowUtils;
import com.hhd.flow.engine.vo.FlowModel;
import com.hhd.flow.engine.vo.FlowUser;
import com.hhd.flow.engine.vo.UserManualSelect;
import com.hhd.flow.entity.FlowProcessDefinition;
import com.hhd.flow.entity.FlowProcessDefinitionRef;
import com.hhd.flow.entity.FlowProcessInstance;
import com.hhd.flow.entity.FlowTask;
import com.hhd.flow.exception.FlowException;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.util.List;
import java.util.Objects;

@Getter
@Setter
@SuperBuilder
public class VirtualStartCommand extends VirtualPassAbleCommand<UserManualSelect> {
    JSONObject formModel;       //表单数据
    String formTypeId;          //单据类型
    String formId;              //单据类型
    FlowUser applyBy;           //发起人

    //下面是运转过程中的数据
    UserManualSelect userManualSelect = new UserManualSelect();
    FlowEngine engine;
    FlowCommandExecutor commandExecutor;
    FlowProcessDefinition flowProcessDefinition;
    FlowProcessDefinitionRef flowProcessDefinitionRef;
    private FlowModel flowModel;
    private VirtualContext context;
    private FlowProcessInstance processInstance;
    private FlowTask startTask;
    String processDefinitionId; //流程定义

    @Override
    public UserManualSelect execute() {
        userManualSelect = new UserManualSelect();

        loadFlowProcessDefinitionRef();

        createProcessInstance();

        createTaskInstance();

        createContext();

        passTask(context);

        return userManualSelect;
    }

    private void createProcessInstance() {
        if (CommonUtils.isEmpty(applyBy)) applyBy = getEngine().getCurrentUser();
        processInstance = FlowUtils.newInstanceForModel(flowModel, this);
    }

    private void createTaskInstance() {
        startTask = FlowUtils.createStartTask(processInstance);
    }

    private void createContext() {
        context = new VirtualContext();
        context.setFormModel(formModel);
        context.setFormTypeId(formTypeId);
        context.setFormId(formId);
        context.setApplyBy(applyBy);
        context.setProcessDefinitionId(processDefinitionId);
        context.setProcessInstance(processInstance);
        context.setFlowModel(flowModel);
        context.getActiveTasks().add(startTask);
        context.setUserManualSelect(userManualSelect);
        context.getHistoryUsers().add(applyBy);
    }

    private void loadFlowProcessDefinitionRef() {
        if (!CommonUtils.isEmpty(processDefinitionId)) {
            flowProcessDefinition = getEngine().getFlowProcessDefinitionService().getById(processDefinitionId);
            CommonUtils.isEmptyThrow(flowProcessDefinition, new RuntimeException("没有找到流程定义：" + processDefinitionId));
        } else {
            loadFlowProcessDefinitionRefWithoutId();
        }
        genFlowProcessDefinitionRef();
        doLoadFlowProcessDefinitionRef();
    }

    private void genFlowProcessDefinitionRef() {
        FlowProcessDefinition flowProcessDefinition = loadFlowProcessDefinitionNotNull(processDefinitionId);
        List<FlowProcessDefinitionRef> refs = getEngine().getFlowProcessDefinitionRefService().lambdaQuery()
                .eq(FlowProcessDefinitionRef::getModelId, processDefinitionId)
                .eq(FlowProcessDefinitionRef::getVersion, flowProcessDefinition.getVersion()).list();
        if (refs.isEmpty()) {
            this.flowProcessDefinitionRef = createFlowProcessDefinitionRef(flowProcessDefinition);
        } else {
            this.flowProcessDefinitionRef = refs.get(0);
        }
        this.processDefinitionId = this.flowProcessDefinitionRef.getId();
    }

    private FlowProcessDefinition loadFlowProcessDefinitionNotNull(String processDefinitionId) {
        FlowProcessDefinition flowProcessDefinition = getEngine().getFlowProcessDefinitionService().getById(processDefinitionId);
        if (Objects.isNull(flowProcessDefinition))
            throw new RuntimeException("没有找到流程定义: " + processDefinitionId);
        return flowProcessDefinition;
    }

    private FlowProcessDefinitionRef createFlowProcessDefinitionRef(FlowProcessDefinition flowProcessDefinition) {
        FlowProcessDefinitionRef flowProcessDefinitionRef = new FlowProcessDefinitionRef();
        flowProcessDefinitionRef.setModelId(flowProcessDefinition.getId());
        flowProcessDefinitionRef.setModel(flowProcessDefinition.getModel());
        flowProcessDefinitionRef.setVersion(flowProcessDefinition.getVersion());

        getEngine().getFlowProcessDefinitionRefService().save(flowProcessDefinitionRef);
        return flowProcessDefinitionRef;
    }

    private void doLoadFlowProcessDefinitionRef() {
        userManualSelect.setFlowProcessDefinitionRefId(flowProcessDefinitionRef.getId());
        flowModel = getModel(flowProcessDefinitionRef);
    }

    private void loadFlowProcessDefinitionRefWithoutId() {
        if (!CommonUtils.isEmpty(processDefinitionId)) {
            flowProcessDefinition = getEngine().getFlowProcessDefinitionService().getById(processDefinitionId);
            CommonUtils.isEmptyThrow(flowProcessDefinition, new RuntimeException("没有找到流程定义: " + processDefinitionId));
            return;
        }
        List<FlowProcessDefinition> list = getEngine().getFlowProcessDefinitionService()
                .lambdaQuery().eq(FlowProcessDefinition::getFormType, formTypeId)
                .eq(FlowProcessDefinition::getEnable, true).list();
        CommonUtils.isEmptyThrow(list, new FlowException("没有找到流程图，请确认单据：" + formTypeId + "是否已经配置流程"));
        if (list.size() > 1) throw new FlowException("找到多个已启用状态的流程图");
        flowProcessDefinition = list.get(0);
        processDefinitionId = flowProcessDefinition.getId();
    }
}
